class StorageService {
  constructor() {
    if (
      !window.localStorage ||
      !window.sessionStorage
    ) console.warn('StorageService', '浏览器不支持 window.localStorage 和 window.sessionStorage')
  }

  get storageMap() {
    if (!this._map) this._map = new Map()
    return this._map
  }

  setPrefix(prefix) {
    this._prefix = prefix ? prefix.toString() : ''
  }

  formatName(name) {
    const { _prefix } = this
    if (_prefix) return `${_prefix}__${name}`
    return name
  }

  // 基于数据 typeof 的类型
  get dataType() {
    return [
      'undefined',
      'boolean',
      'number',
      'string',
      'object',
      'bigint',
      // 'symbol'
      // 'function'
    ]
  }

  register(name, type = 'string', mode = 'local') {
    if (!name) {
      console.warn('StorageService register name is required')
      return
    }
    if (this.storageMap.has(name)) {
      console.warn('StorageService register name is existing')
      return
    }
    if (!this.dataType.includes(type)) {
      console.warn('StorageService register type is illegal')
      return
    }
    if (mode !== 'local' && mode !== 'session') {
      console.warn('StorageService register mode not support')
      return
    }
    // 注册 storageMap
    this.storageMap.set(name, { name, type, mode })
  }

  registers(storages) {
    if (Array.isArray(storages)) storages.forEach(storage => {
      storage && this.register(storage.name, storage.type, storage.mode)
    })
  }

  find(name) {
    if (!this.storageMap.has(name)) {
      console.warn('StorageService find name is null')
      return null
    }
    return this.storageMap.get(name)
  }

  set(name, value) {
    const config = this.find(name)
    if (!config) return

    const type = typeof value
    if (type !== config.type) {
      console.warn('StorageService storage.set value type is not correct')
      return
    }

    switch (type) {
      case 'object':
        if (value) value = JSON.stringify(value)
        else value = String(null)
        break
      default:
        value = String(value)
    }
    this._storageSet(config.mode, name, value)
  }

  get(name) {
    let value = null
    const config = this.find(name)
    if (!config) return value

    value = this._storageGet(config.mode, name)
    // 转换value
    switch (config.type) {
      case 'undefined':
        value = undefined
        break
      case 'boolean':
        if (value === 'true') value = true
        else value = false
        break
      case 'number':
        value = value ? Number(value) : null
        break
      case 'string':
        value = value ? String(value) : ''
        break
      case 'object':
        if (value && value !== 'null') {
          try { value = JSON.parse(value) }
          catch (err) { value = null }
        } else value = null
        break
      case 'bigint':
        if (!window.BigInt) throw new Error('BigInt not support')
        if (value) {
          try { value = window.BigInt(value) }
          catch (err) { value = window.BigInt('') }
        } else value = window.BigInt('')
        break
    }
    return value
  }

  remove(name) {
    const config = this.find(name)
    if (!config) return

    this._storageRemove(config.mode, name)
  }

  clear() {
    this._storageClear()
  }

  _storageSet(mode, name, value) {
    const _name = this.formatName(name)
    switch (mode) {
      case 'local':
        window.localStorage.setItem(_name, value)
        break
      case 'session':
        window.sessionStorage.setItem(_name, value)
        break
    }
  }

  _storageGet(mode, name) {
    const _name = this.formatName(name)
    switch (mode) {
      case 'local':
        return window.localStorage.getItem(_name)
      case 'session':
        return window.sessionStorage.getItem(_name)
      default:
        return undefined
    }
  }

  _storageRemove(mode, name) {
    const _name = this.formatName(name)
    switch (mode) {
      case 'local':
        window.localStorage.removeItem(_name)
        break
      case 'session':
        window.sessionStorage.removeItem(_name)
        break
    }
  }

  _storageClear() {
    window.localStorage.clear()
    window.sessionStorage.clear()
  }
}

export default StorageService
